home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / common / plugindetails.c < prev    next >
Encoding:
C/C++ Source or Header  |  2001-11-09  |  31.3 KB  |  1,154 lines

  1. /*
  2.  * Copyright (C) 1995 Spencer Kimball and Peter Mattis
  3.  *
  4.  * This is a plug-in for the GIMP.
  5.  *
  6.  * Copyright (C) 1999 Andy Thomas  alt@picnic.demon.co.uk
  7.  *
  8.  * Note some portions of the UI comes from the dbbrowser plugin.
  9.  * This program is free software; you can redistribute it and/or modify
  10.  * it under the terms of the GNU General Public License as published by
  11.  * the Free Software Foundation; either version 2 of the License, or
  12.  * (at your option) any later version.
  13.  *
  14.  * This program is distributed in the hope that it will be useful,
  15.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17.  * GNU General Public License for more details.
  18.  *
  19.  * You should have received a copy of the GNU General Public License
  20.  * along with this program; if not, write to the Free Software
  21.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  22.  */
  23.  
  24. #include "config.h"
  25.  
  26. #include <stdlib.h>
  27. #include <stdio.h>
  28. #include <string.h>
  29. #include <time.h>
  30.  
  31. #include <gtk/gtk.h>
  32.  
  33. #include <libgimp/gimp.h>
  34. #include <libgimp/gimpui.h>
  35.  
  36. #include "libgimp/stdplugins-intl.h"
  37.  
  38.  
  39. #define DBL_LIST_WIDTH  250
  40. #define DBL_WIDTH       (DBL_LIST_WIDTH + 300)
  41. #define DBL_HEIGHT      200
  42.  
  43. static gchar *proc_type_str[] =
  44. {
  45.   N_("Internal GIMP procedure"),
  46.   N_("GIMP Plug-In"),
  47.   N_("GIMP Extension"),
  48.   N_("Temporary Procedure")
  49. };
  50.  
  51.  
  52. /* Declare some local functions.
  53.  */
  54. static void   query      (void);
  55. static void   run        (gchar      *name,
  56.                           gint        nparams,
  57.                           GimpParam  *param,
  58.                           gint       *nreturn_vals,
  59.                           GimpParam **return_vals);
  60.  
  61. static GtkWidget * gimp_plugin_desc (void);
  62.  
  63. static gint procedure_clist_select_callback (GtkWidget      *widget,
  64.                          gint            row, 
  65.                          gint            column, 
  66.                          GdkEventButton *bevent,
  67.                          gpointer        data);
  68. static gint procedure_ctree_select_callback (GtkWidget      *widget,
  69.                          GtkWidget      *row, 
  70.                          gint            column, 
  71.                          gpointer        data);
  72.  
  73. GimpPlugInInfo PLUG_IN_INFO =
  74. {
  75.   NULL,  /* init_proc  */
  76.   NULL,  /* quit_proc  */
  77.   query, /* query_proc */
  78.   run,   /* run_proc   */
  79. };
  80.  
  81.  
  82. MAIN ()
  83.  
  84. static void
  85. query (void)
  86. {
  87.   static GimpParamDef args[] =
  88.   {
  89.     { GIMP_PDB_INT32, "run_mode", "Interactive, [non-interactive]" }
  90.   };
  91.   static gint nargs = sizeof (args) / sizeof (args[0]);
  92.  
  93.   gimp_install_procedure ("plug_in_details",
  94.                           "Displays plugin details",
  95.                           "Helps browse the plugin menus system. You can "
  96.               "search for plugin names, sort by name or menu "
  97.               "location and you can view a tree representation "
  98.               "of the plugin menus. Can also be of help to find "
  99.               "where new plugins have installed themselves in "
  100.               "the menuing system",
  101.                           "Andy Thomas",
  102.                           "Andy Thomas",
  103.                           "1999",
  104.               N_("<Toolbox>/Xtns/Plugin Details..."),
  105.               "",
  106.                           GIMP_EXTENSION,
  107.               nargs, 0,
  108.                           args, NULL);
  109. }
  110.  
  111. static void
  112. run (gchar      *name,
  113.      gint        nparams,
  114.      GimpParam  *param,
  115.      gint       *nreturn_vals,
  116.      GimpParam **return_vals)
  117. {
  118.   static GimpParam  values[2];
  119.   GimpRunModeType   run_mode;
  120.   GtkWidget        *plugin_dialog;
  121.  
  122.   run_mode = param[0].data.d_int32;
  123.  
  124.   *nreturn_vals = 1;
  125.   *return_vals  = values;
  126.   values[0].type          = GIMP_PDB_STATUS;
  127.   values[0].data.d_status = GIMP_PDB_CALLING_ERROR;
  128.  
  129.   INIT_I18N_UI();
  130.  
  131.   if (strcmp (name, "plug_in_details") == 0)
  132.     {
  133.       *nreturn_vals = 1;
  134.  
  135.       values[0].data.d_status = GIMP_PDB_SUCCESS;
  136.  
  137.       plugin_dialog = gimp_plugin_desc ();
  138.  
  139.       gtk_main ();
  140.       gdk_flush ();
  141.     }
  142. }
  143.  
  144.  
  145. typedef struct  
  146. {
  147.   GtkWidget *dlg;
  148.   GtkWidget *clist;
  149.   GtkWidget *ctree;
  150.   GtkWidget *search_entry;
  151.   GtkWidget *descr_scroll;
  152.   GtkWidget *name_button;
  153.   GtkWidget *blurb_button;
  154.   GtkWidget *scrolled_win;
  155.   GtkWidget *ctree_scrolled_win;
  156.   GtkWidget *info_table;
  157.   GtkWidget *paned;
  158.   GtkWidget *left_paned;
  159.   GtkWidget *info_align;
  160.   gint       num_plugins;
  161.   gint       ctree_row;
  162.   gint       clist_row;
  163.   gint       c1size;
  164.   gboolean   details_showing;
  165. } PDesc;
  166.  
  167. PDesc *plugindesc = NULL;
  168.  
  169. typedef struct
  170. {
  171.   gchar *menu;
  172.   gchar *accel;
  173.   gchar *prog;
  174.   gchar *types;
  175.   gchar *realname;
  176.   gint  instime;
  177. } PInfo;
  178.  
  179. static void
  180. dialog_close_callback (GtkWidget *widget, 
  181.                gpointer   data)
  182.      /* end of the dialog */
  183. {
  184.   PDesc *pdesc = data;
  185.   gtk_widget_destroy (pdesc->dlg);
  186.   gtk_main_quit ();
  187. }
  188.  
  189. /* Bit of a fiddle but sorta has the effect I want... */
  190.  
  191. static void
  192. details_callback (GtkWidget *widget, 
  193.           gpointer   data)
  194. {
  195.   /* Show or hide the details window */ 
  196.   PDesc *pdesc = data;
  197.   GtkLabel *lab = GTK_LABEL (GTK_BIN (widget)->child);
  198.  
  199.   /* This is a lame hack: 
  200.      We add the description on the right on the first details_callback.
  201.      Otherwise the window reacts quite weird on resizes */
  202.   if (pdesc->descr_scroll == NULL)
  203.     {
  204.       pdesc->descr_scroll = gtk_scrolled_window_new (NULL, NULL);
  205.       gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (pdesc->descr_scroll),
  206.                       GTK_POLICY_ALWAYS, 
  207.                       GTK_POLICY_ALWAYS);
  208.       gtk_widget_set_usize (pdesc->descr_scroll, DBL_WIDTH - DBL_LIST_WIDTH, -1);
  209.       gtk_paned_pack2 (GTK_PANED (pdesc->paned), pdesc->descr_scroll,
  210.                FALSE, TRUE);
  211.       gtk_clist_select_row (GTK_CLIST(pdesc->clist), pdesc->clist_row, -1);
  212.     }
  213.  
  214.   if (pdesc->details_showing == FALSE)
  215.     {
  216.       GTK_PANED (pdesc->paned)->child1_resize = FALSE;
  217.       gtk_paned_set_handle_size (GTK_PANED (pdesc->paned), 10);
  218. #if !defined (GTK_CHECK_VERSION) || !GTK_CHECK_VERSION (1, 3, 0)
  219.       gtk_paned_set_gutter_size (GTK_PANED (pdesc->paned), 6);
  220. #endif
  221.       gtk_label_set_text (lab, _("Details <<"));
  222.       gtk_widget_show (pdesc->descr_scroll);
  223.       pdesc->details_showing = TRUE;
  224.     }
  225.   else
  226.     {
  227.       GtkWidget *p = GTK_WIDGET (pdesc->paned)->parent;
  228.       GTK_PANED (pdesc->paned)->child1_resize = TRUE;
  229.       GTK_PANED (pdesc->paned)->child2_resize = TRUE;
  230.       gtk_paned_set_handle_size (GTK_PANED (pdesc->paned), 0);
  231. #if !defined (GTK_CHECK_VERSION) || !GTK_CHECK_VERSION (1, 3, 0)
  232.       gtk_paned_set_gutter_size (GTK_PANED (pdesc->paned), 0);
  233. #endif
  234.       gtk_label_set_text (lab, _("Details >>"));
  235.       gtk_widget_hide (pdesc->descr_scroll);
  236.       gtk_paned_set_position (GTK_PANED (pdesc->paned),
  237.                   p->allocation.width);/*plugindesc->c1size);*/
  238.       pdesc->details_showing = FALSE;
  239.     }
  240. }
  241.  
  242. static gchar *
  243. format_menu_path (gchar *s)
  244. {
  245.   gchar **str_array;
  246.   gchar  *newstr = NULL;
  247.  
  248.   if (!s)
  249.     return s;
  250.  
  251.   str_array = g_strsplit (s, "/", 0);
  252.  
  253.   newstr = g_strjoinv ("->", str_array);
  254.  
  255.   g_strfreev (str_array);
  256.  
  257.   return newstr;
  258. }
  259.  
  260. static gint
  261. procedure_general_select_callback (PDesc *pdesc,
  262.                    PInfo *pinfo)
  263. {
  264.   gchar           *selected_proc_blurb;
  265.   gchar           *selected_proc_help; 
  266.   gchar           *selected_proc_author;
  267.   gchar           *selected_proc_copyright;
  268.   gchar           *selected_proc_date;
  269.   GimpPDBProcType  selected_proc_type; 
  270.   gint             selected_nparams;
  271.   gint             selected_nreturn_vals;
  272.   GimpParamDef    *selected_params;
  273.   GimpParamDef    *selected_return_vals;
  274.   GtkWidget *label;
  275.   GtkWidget *help;
  276.   GtkWidget *text;
  277.   GtkWidget *vscrollbar;
  278.   GtkWidget *old_table;
  279.   GtkWidget *old_align;
  280.   gint       table_row = 0;
  281.   gchar     *str;
  282.  
  283.   g_return_val_if_fail (pdesc != NULL, FALSE);
  284.   g_return_val_if_fail (pinfo != NULL, FALSE);
  285.  
  286.   if (pdesc->descr_scroll == NULL)
  287.     return FALSE;
  288.  
  289.   selected_proc_blurb     = NULL;
  290.   selected_proc_help      = NULL;
  291.   selected_proc_author    = NULL;
  292.   selected_proc_copyright = NULL;
  293.   selected_proc_date      = NULL;
  294.   selected_proc_type      = 0;
  295.   selected_nparams        = 0;
  296.   selected_nreturn_vals   = 0;
  297.   selected_params         = NULL;
  298.   selected_return_vals    = NULL;
  299.  
  300.   gimp_procedural_db_proc_info (pinfo->realname, 
  301.                 &selected_proc_blurb, 
  302.                 &selected_proc_help, 
  303.                 &selected_proc_author,
  304.                 &selected_proc_copyright, 
  305.                 &selected_proc_date, 
  306.                 &selected_proc_type, 
  307.                 &selected_nparams, &selected_nreturn_vals, 
  308.                 &selected_params, &selected_return_vals);
  309.  
  310.   old_table = pdesc->info_table;
  311.   old_align = pdesc->info_align;
  312.  
  313.   pdesc->info_table = gtk_table_new (10, 5, FALSE);
  314.   pdesc->info_align = gtk_alignment_new (0.5, 0.5, 0, 0);
  315.  
  316.   gtk_table_set_col_spacings (GTK_TABLE (pdesc->info_table), 3);
  317.  
  318.   /* Number of plugins */
  319.  
  320.   str = g_strdup_printf (_("Number of Plugin Interfaces: %d"),
  321.              pdesc->num_plugins);
  322.   label = gtk_label_new (str);
  323.   g_free (str);
  324.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  325.   gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
  326.             0, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 0, 0);
  327.   gtk_widget_show (label);
  328.   table_row++;
  329.  
  330.   label = gtk_hseparator_new (); /* ok, not really a label ... :) */
  331.   gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
  332.             0, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 6);
  333.   gtk_widget_show (label);
  334.   table_row++;
  335.  
  336.   /* menu path */
  337.  
  338.   label = gtk_label_new (_("Menu Path:"));
  339.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); 
  340.   gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
  341.             0, 1, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 0);
  342.   gtk_widget_show(label);
  343.  
  344.   label = gtk_label_new (format_menu_path (pinfo->menu));
  345.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  346.   gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
  347.             1, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 0, 0);
  348.   gtk_widget_show (label);
  349.   table_row++;
  350.  
  351.   label = gtk_hseparator_new (); /* ok, not really a label ... :) */
  352.   gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
  353.             0, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 6);
  354.   gtk_widget_show (label);
  355.   table_row++;
  356.  
  357.   /* show the name */
  358.  
  359.   label = gtk_label_new (_("Name:"));
  360.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); 
  361.   gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
  362.             0, 1, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 6);
  363.   gtk_widget_show(label);
  364.  
  365.   label = gtk_entry_new ();
  366.   gtk_entry_set_text (GTK_ENTRY (label), pinfo->realname);
  367.   gtk_entry_set_editable (GTK_ENTRY (label), FALSE);
  368.   gtk_table_attach (GTK_TABLE(pdesc->info_table), label,
  369.             1, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 0, 0);
  370.   gtk_widget_show (label);
  371.   table_row++;
  372.  
  373.   label = gtk_hseparator_new (); /* ok, not really a label ... :) */
  374.   gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
  375.             0, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 6);
  376.   gtk_widget_show (label);
  377.   table_row++;
  378.  
  379.   /* show the description */
  380.  
  381.   label = gtk_label_new (_("Blurb:"));
  382.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); 
  383.   gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
  384.             0, 1, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 0);
  385.   gtk_widget_show (label);
  386.  
  387.   label = gtk_label_new (selected_proc_blurb);
  388.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  389.   gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
  390.             1, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 0, 0);
  391.   gtk_widget_show(label);
  392.   table_row++;
  393.  
  394.   label = gtk_hseparator_new (); /* ok, not really a label ... :) */
  395.   gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
  396.             0, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 6);
  397.   gtk_widget_show (label);
  398.   table_row++;
  399.  
  400.   /* show the help */
  401.   if ((selected_proc_help) && (strlen(selected_proc_help) > 1))
  402.     {
  403.       label = gtk_label_new (_("Help:"));
  404.       gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); 
  405.       gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
  406.             0, 1, table_row, table_row+1, 
  407.             GTK_FILL, GTK_FILL, 3, 0);
  408.       gtk_widget_show (label);
  409.       
  410.       help = gtk_table_new (2, 2, FALSE);
  411.       gtk_table_set_row_spacing (GTK_TABLE (help), 0, 2);
  412.       gtk_table_set_col_spacing (GTK_TABLE (help), 0, 2);
  413.       gtk_table_attach (GTK_TABLE (pdesc->info_table), help,
  414.             1, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 0);
  415.       gtk_widget_show (help);
  416.       table_row++;
  417.       
  418.       text = gtk_text_new (NULL, NULL);
  419.       gtk_text_set_editable (GTK_TEXT (text), FALSE);
  420.       gtk_text_set_word_wrap(GTK_TEXT(text), TRUE);
  421.       gtk_widget_set_usize (text, -1, 60);
  422.       gtk_table_attach (GTK_TABLE (help), text, 0, 1, 0, 1,
  423.             GTK_EXPAND | GTK_SHRINK | GTK_FILL,
  424.             GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
  425.       gtk_widget_show (text);
  426.       
  427.       vscrollbar = gtk_vscrollbar_new (GTK_TEXT (text)->vadj);
  428.       gtk_table_attach (GTK_TABLE (help), vscrollbar, 1, 2, 0, 1,
  429.             GTK_FILL, GTK_EXPAND | GTK_SHRINK | GTK_FILL, 0, 0);
  430.       gtk_widget_show (vscrollbar);
  431.             
  432.       label = gtk_hseparator_new (); /* ok, not really a label ... :) */
  433.       gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
  434.             0, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 6);
  435.       gtk_widget_show(label);
  436.  
  437.       gtk_text_freeze (GTK_TEXT (text));
  438.       gtk_text_insert (GTK_TEXT (text), NULL, NULL, NULL,
  439.                selected_proc_help, -1);
  440.       gtk_text_thaw (GTK_TEXT (text));
  441.  
  442.       table_row++;
  443.     }
  444.  
  445.   /* show the type */
  446.  
  447.   label = gtk_label_new (_("Type:"));
  448.   gtk_misc_set_alignment (GTK_MISC (label), 1.0, 0.5); 
  449.   gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
  450.             0, 1, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 6);
  451.   gtk_widget_show(label);
  452.  
  453.   label = gtk_label_new (gettext (proc_type_str[selected_proc_type]));
  454.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  455.   gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
  456.             1, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 0, 0);
  457.   gtk_widget_show (label);
  458.   table_row++;
  459.  
  460.   label = gtk_hseparator_new (); /* ok, not really a label ... :) */
  461.   gtk_table_attach (GTK_TABLE (pdesc->info_table), label,
  462.             0, 4, table_row, table_row+1, GTK_FILL, GTK_FILL, 3, 6);
  463.   gtk_widget_show (label);
  464.   table_row++;
  465.  
  466.   /* Remove old and replace with new */
  467.  
  468.   if (old_table)
  469.     gtk_widget_destroy (old_table);
  470.  
  471.   if (old_align)
  472.     gtk_widget_destroy (old_align);
  473.  
  474.   gtk_container_add (GTK_CONTAINER (pdesc->info_align),pdesc->info_table);
  475.  
  476.   gtk_scrolled_window_add_with_viewport (GTK_SCROLLED_WINDOW (pdesc->descr_scroll), 
  477.                      pdesc->info_align);
  478.  
  479.   gtk_widget_show (pdesc->info_table);
  480.   gtk_widget_show (pdesc->info_align);
  481.  
  482.   if (selected_proc_blurb)
  483.     g_free (selected_proc_blurb);
  484.   if (selected_proc_help)
  485.     g_free (selected_proc_help); 
  486.   if (selected_proc_author)
  487.     g_free (selected_proc_author);
  488.   if (selected_proc_copyright)
  489.     g_free (selected_proc_copyright);
  490.   if (selected_proc_date)
  491.     g_free (selected_proc_date);
  492.   if (selected_params)
  493.     g_free (selected_params);
  494.   if (selected_return_vals)
  495.     g_free (selected_return_vals);
  496.  
  497.   return FALSE;
  498. }
  499.  
  500. static void 
  501. expand_to (PDesc        *pdesc,
  502.        GtkCTreeNode *parent)
  503. {
  504.   if(parent)
  505.     {
  506.       expand_to (pdesc, (GTK_CTREE_ROW (parent))->parent);
  507.       gtk_ctree_expand (GTK_CTREE (pdesc->ctree), parent);      
  508.     }
  509. }
  510.  
  511. static gint
  512. procedure_clist_select_callback (GtkWidget      *widget,
  513.                  gint            row, 
  514.                  gint            column, 
  515.                  GdkEventButton *bevent,
  516.                  gpointer        data)
  517. {
  518.   PInfo *pinfo;
  519.   GtkCTreeNode * found_node; 
  520.   PDesc *pdesc = data;
  521.  
  522.   g_return_val_if_fail (pdesc != NULL, FALSE);
  523.  
  524.   pinfo = (PInfo *) gtk_clist_get_row_data (GTK_CLIST (widget), row);
  525.  
  526.   if (!pinfo)
  527.     return FALSE;
  528.  
  529.   /* Must select the correct one in the ctree structure */
  530.  
  531.   found_node = gtk_ctree_find_by_row_data (GTK_CTREE (pdesc->ctree),
  532.                        NULL, pinfo);
  533.   
  534.   if (found_node)
  535.     {
  536.       GtkCTreeRow   *ctr;
  537.       GtkCTreeNode  *parent;
  538.       gint sel_row;
  539.  
  540.       /* Make sure this is expanded */
  541.  
  542.       ctr = GTK_CTREE_ROW (found_node);
  543.  
  544.       parent = GTK_CTREE_NODE (ctr->parent);
  545.  
  546.       expand_to (pdesc, parent);
  547.  
  548.       sel_row = gtk_clist_find_row_from_data (GTK_CLIST (pdesc->ctree), pinfo);
  549.  
  550.       gtk_widget_hide (pdesc->ctree); 
  551.       gtk_widget_show (pdesc->ctree); 
  552.  
  553.       gtk_signal_handler_block_by_func (GTK_OBJECT(pdesc->ctree),
  554.                     GTK_SIGNAL_FUNC (procedure_ctree_select_callback),
  555.                     pdesc);
  556.  
  557.       gtk_clist_select_row (GTK_CLIST (pdesc->ctree), sel_row, -1);  
  558.       gtk_ctree_select (GTK_CTREE (pdesc->ctree), found_node);
  559.  
  560.       gtk_clist_moveto (GTK_CLIST (pdesc->ctree),
  561.             sel_row,
  562.             0,
  563.             0.5, 0.5);
  564.  
  565.       gtk_signal_handler_unblock_by_func (GTK_OBJECT (pdesc->ctree),
  566.                       GTK_SIGNAL_FUNC (procedure_ctree_select_callback),
  567.                       pdesc);
  568.  
  569.       pdesc->ctree_row = sel_row;
  570.     }
  571.   else
  572.     {
  573.       g_warning ("Failed to find node in ctree");
  574.     }
  575.  
  576.   return procedure_general_select_callback (pdesc, pinfo);
  577. }
  578.  
  579. /* This was an attempt to get around a problem in gtk 
  580.  * where the scroll windows that contain ctree and clist
  581.  * refuse to respond to the moveto funcions unless the
  582.  * widgets are visible.
  583.  * Still did not work 100% even after this.
  584.  * Note the scrollbars are in the correct position but the
  585.  * widget needs to be redrawn at the correct location.
  586.  */
  587.  
  588. static gint
  589. page_select_callback (GtkNotebook     *notebook,
  590.               GtkNotebookPage *page,
  591.               guint            page_num,
  592.               gpointer         data)
  593. {
  594.   PDesc *pdesc = data;
  595.  
  596.   if (page_num == 0)
  597.     {
  598.       gtk_clist_select_row (GTK_CLIST (pdesc->clist), pdesc->clist_row, -1);  
  599.       gtk_clist_moveto (GTK_CLIST (pdesc->clist),
  600.             pdesc->clist_row,
  601.             0,
  602.             0.5, 0.0);
  603.     }
  604.   else
  605.     {
  606.       gtk_clist_select_row (GTK_CLIST (pdesc->ctree), pdesc->ctree_row, -1);  
  607.       gtk_clist_moveto (GTK_CLIST (pdesc->ctree),
  608.             pdesc->ctree_row,
  609.             0,
  610.             0.5, 0.0);
  611.     }
  612.  
  613.   return FALSE;
  614. }
  615.  
  616. static gint
  617. procedure_ctree_select_callback (GtkWidget *widget,
  618.                  GtkWidget *row, 
  619.                  gint       column, 
  620.                  gpointer   data)
  621. {
  622.   PInfo *pinfo;
  623.   PDesc *pdesc;
  624.   gboolean is_leaf;
  625.   gint sel_row;
  626.  
  627.   /* row is not a leaf the we have no interest in it */
  628.  
  629.   gtk_ctree_get_node_info (GTK_CTREE (widget),
  630.                GTK_CTREE_NODE (row),
  631.                NULL,
  632.                NULL,
  633.                NULL,
  634.                NULL,
  635.                NULL,
  636.                NULL,
  637.                &is_leaf,
  638.                NULL);
  639.  
  640.   if (!is_leaf)
  641.     return FALSE;
  642.  
  643.   pdesc = data;
  644.  
  645.   pinfo = (PInfo *) gtk_ctree_node_get_row_data (GTK_CTREE (widget),
  646.                          GTK_CTREE_NODE (row));
  647.  
  648.   /* Must set clist to this one */
  649.   /* Block signals */
  650.  
  651.   gtk_signal_handler_block_by_func (GTK_OBJECT (pdesc->clist),
  652.                     GTK_SIGNAL_FUNC (procedure_clist_select_callback),
  653.                     pdesc);
  654.  
  655.   sel_row = gtk_clist_find_row_from_data (GTK_CLIST (pdesc->clist), pinfo);
  656.   gtk_clist_select_row (GTK_CLIST (pdesc->clist), sel_row, -1);  
  657.   gtk_clist_moveto (GTK_CLIST (pdesc->clist),
  658.             sel_row,
  659.             0,
  660.             0.5, 0.5);
  661.  
  662.   gtk_signal_handler_unblock_by_func (GTK_OBJECT (pdesc->clist),
  663.                       GTK_SIGNAL_FUNC (procedure_clist_select_callback),
  664.                       pdesc);
  665.  
  666.   pdesc->clist_row = sel_row;
  667.   
  668.   return procedure_general_select_callback (pdesc, pinfo);
  669. }
  670.  
  671. static void
  672. pinfo_free (gpointer p)
  673. {
  674.   PInfo *pinfo = p;
  675.  
  676.   g_free (pinfo->menu);
  677.   g_free (pinfo->accel);
  678.   g_free (pinfo->prog);
  679.   g_free (pinfo->types);
  680.   g_free (pinfo->realname);
  681.   g_free (pinfo);
  682. }
  683.  
  684. static GtkCTreeNode *
  685. get_parent (PDesc       *pdesc,
  686.         GHashTable  *ghash,
  687.         gchar       *mpath)
  688. {
  689.   GtkCTreeNode *parent;
  690.   GtkCTreeNode *last_parent;
  691.   gchar *tmp_ptr;
  692.   gchar *str_ptr;
  693.   gchar *leaf_ptr;
  694.   gchar *labels[3];
  695.   
  696.   if (mpath == NULL)
  697.     return NULL; /* Parent is root */
  698.  
  699.   parent = g_hash_table_lookup (ghash, mpath);
  700.  
  701.   if (parent)
  702.     {
  703.       /* found node */
  704.       return parent;
  705.     }
  706.  
  707.   /* Next one up */
  708.   tmp_ptr = g_strdup (mpath);
  709.  
  710.   str_ptr = strrchr (tmp_ptr,'/');
  711.  
  712.   if (str_ptr == NULL)
  713.     {
  714.       /*       printf("Root node for %s\n",mpath); */
  715.       leaf_ptr = mpath;
  716.       tmp_ptr = "<root>";
  717.       last_parent = NULL;
  718.     }
  719.   else
  720.     {
  721.       leaf_ptr = g_strdup(str_ptr+1);
  722.  
  723.       *str_ptr = '\000';
  724.  
  725.       last_parent = get_parent (pdesc, ghash, tmp_ptr);
  726.     }
  727.  
  728.   labels[0] = g_strdup (leaf_ptr); 
  729.   labels[1] = g_strdup (""); 
  730.   labels[2] = g_strdup (""); 
  731.  
  732.   /*   printf("get_parent::creating node %s under %s\n",leaf_ptr,tmp_ptr); */
  733.  
  734.   parent = gtk_ctree_insert_node (GTK_CTREE (pdesc->ctree),
  735.                   last_parent,
  736.                   NULL,
  737.                   labels,
  738.                   4,
  739.                   NULL,
  740.                   NULL,
  741.                   NULL,
  742.                   NULL,
  743.                   FALSE,
  744.                   FALSE);
  745.  
  746.   g_hash_table_insert (ghash, mpath, parent); 
  747.  
  748.   return parent;
  749. }
  750.  
  751. static void
  752. insert_into_ctree (PDesc      *pdesc,
  753.            gchar      *name,
  754.            gchar      *xtimestr,
  755.            gchar      *menu_str,
  756.            gchar      *types_str,
  757.            GHashTable *ghash,
  758.            PInfo      *pinfo)
  759. {
  760.   gchar *labels[3];
  761.   gchar *str_ptr;
  762.   gchar *tmp_ptr;
  763.   gchar *leaf_ptr;
  764.   GtkCTreeNode *parent = NULL;
  765.   GtkCTreeNode *leaf_node = NULL;
  766.  
  767.   /* Find all nodes */
  768.   /* Last one is the leaf part */
  769.  
  770.   tmp_ptr = g_strdup (menu_str);
  771.  
  772.   str_ptr = strrchr (tmp_ptr, '/');
  773.  
  774.   if (str_ptr == NULL)
  775.     return; /* No node */
  776.  
  777.   leaf_ptr = g_strdup (str_ptr + 1);
  778.  
  779.   *str_ptr = '\000';
  780.  
  781.   /*   printf("inserting %s...\n",menu_str); */
  782.  
  783.   parent = get_parent (pdesc, ghash, tmp_ptr);
  784.  
  785.   /* Last was a leaf */
  786.   /*   printf("found leaf %s parent = %p\n",leaf_ptr,parent); */
  787.   labels[0] = g_strdup (name);
  788.   labels[1] = g_strdup (xtimestr);
  789.   labels[2] = g_strdup (types_str);
  790.  
  791.   leaf_node = gtk_ctree_insert_node (GTK_CTREE (pdesc->ctree),
  792.                      parent,
  793.                      NULL,
  794.                      labels,
  795.                      4,
  796.                      NULL,
  797.                      NULL,
  798.                      NULL,
  799.                      NULL,
  800.                      TRUE,
  801.                      FALSE);
  802.  
  803.   gtk_ctree_node_set_row_data (GTK_CTREE (pdesc->ctree), leaf_node, pinfo);
  804. }
  805.  
  806. static void
  807. get_plugin_info (PDesc *pdesc,
  808.          gchar *search_text)
  809. {
  810.   GimpParam *return_vals;
  811.   gint nreturn_vals;
  812.   gint  row_count = 0;
  813.   gchar **menu_strs;
  814.   gchar **accel_strs;
  815.   gchar **prog_strs;
  816.   gchar **types_strs;
  817.   gchar **realname_strs;
  818.   gint  *time_ints;
  819.  
  820.   GHashTable* ghash = g_hash_table_new (g_str_hash, g_str_equal);
  821.  
  822.   if (!search_text)
  823.     search_text = "";
  824.  
  825.   return_vals = gimp_run_procedure ("gimp_plugins_query",
  826.                                     &nreturn_vals,
  827.                     GIMP_PDB_STRING,search_text,
  828.                                     GIMP_PDB_END);
  829.  
  830.   if (return_vals[0].data.d_status == GIMP_PDB_SUCCESS)
  831.     {
  832.       int loop;
  833.       pdesc->num_plugins = return_vals[1].data.d_int32;
  834.       menu_strs          = return_vals[2].data.d_stringarray;
  835.       accel_strs         = return_vals[4].data.d_stringarray;
  836.       prog_strs          = return_vals[6].data.d_stringarray;
  837.       types_strs         = return_vals[8].data.d_stringarray;
  838.       time_ints          = return_vals[10].data.d_int32array;
  839.       realname_strs      = return_vals[12].data.d_stringarray;
  840.  
  841.       for (loop = 0; loop < return_vals[1].data.d_int32; loop++)
  842.     {
  843.       PInfo *pinfo;
  844.       gchar *labels[4];
  845.       gchar *name;
  846.       gchar xtimestr[50];
  847.       struct tm * x;
  848.       time_t tx;
  849.       int ret;
  850.  
  851.       name = strrchr (menu_strs[loop], '/');
  852.  
  853.       if (name)
  854.         name = name + 1;
  855.       else
  856.         name = menu_strs[loop];
  857.  
  858.       pinfo = g_new0 (PInfo, 1);
  859.  
  860.       tx = time_ints[loop];
  861.       if (tx)
  862.         {
  863.           x = localtime (&tx);
  864.           ret = strftime (xtimestr, sizeof (xtimestr), "%c", x);
  865.           xtimestr[ret] = 0;
  866.         }
  867.       else
  868.         strcpy (xtimestr,"");
  869.  
  870.       pinfo->menu     = g_strdup (menu_strs[loop]);
  871.       pinfo->accel    = g_strdup (accel_strs[loop]);
  872.       pinfo->prog     = g_strdup (prog_strs[loop]);
  873.       pinfo->types    = g_strdup (types_strs[loop]);
  874.       pinfo->instime  = time_ints[loop];
  875.       pinfo->realname = g_strdup (realname_strs[loop]);
  876.  
  877.       labels[0] = g_strdup (name);
  878.       labels[1] = g_strdup (xtimestr);
  879.       labels[2] = g_strdup (menu_strs[loop]);
  880.       labels[3] = g_strdup (types_strs[loop]);
  881.  
  882.       gtk_clist_insert (GTK_CLIST (pdesc->clist), row_count, labels);
  883.  
  884.       gtk_clist_set_row_data_full (GTK_CLIST (pdesc->clist), row_count,
  885.                        pinfo, pinfo_free);
  886.  
  887.       row_count++;
  888.  
  889.       /* Now do the tree view.... */
  890.       gtk_signal_handler_block_by_func (GTK_OBJECT(pdesc->ctree),
  891.                         GTK_SIGNAL_FUNC (procedure_ctree_select_callback),
  892.                         pdesc);
  893.       insert_into_ctree (pdesc,
  894.                  name,
  895.                  xtimestr,
  896.                  menu_strs[loop],
  897.                  types_strs[loop],
  898.                  ghash,
  899.                  pinfo);
  900.       gtk_signal_handler_unblock_by_func (GTK_OBJECT (pdesc->ctree),
  901.                           GTK_SIGNAL_FUNC (procedure_ctree_select_callback),
  902.                           pdesc);
  903.     }
  904.     }
  905.  
  906.   gimp_destroy_params (return_vals, nreturn_vals);
  907. }
  908.  
  909. static void 
  910. dialog_search_callback (GtkWidget *widget, 
  911.             gpointer   data)
  912. {
  913.   PDesc *pdesc = data;
  914.   gchar *search_text = NULL;
  915.  
  916.   if (widget != NULL)
  917.     {
  918.       /* The result of a button press... read entry data */
  919.       search_text = gtk_entry_get_text (GTK_ENTRY (plugindesc->search_entry));
  920.     }
  921.  
  922.   gtk_clist_freeze (GTK_CLIST (pdesc->ctree));
  923.   gtk_clist_clear (GTK_CLIST (pdesc->ctree));
  924.   gtk_clist_freeze (GTK_CLIST (pdesc->clist));
  925.   gtk_clist_clear (GTK_CLIST (pdesc->clist));
  926.  
  927.   get_plugin_info (pdesc, search_text);
  928.  
  929.   gtk_clist_columns_autosize (GTK_CLIST (plugindesc->clist));
  930.  
  931.   gtk_clist_sort (GTK_CLIST (pdesc->clist));
  932.   gtk_clist_thaw (GTK_CLIST (pdesc->clist));
  933.   gtk_ctree_sort_recursive (GTK_CTREE (pdesc->ctree), NULL);
  934.   gtk_clist_thaw (GTK_CLIST (pdesc->ctree));
  935. }
  936.  
  937. static gint
  938. date_sort (GtkCList      *clist,
  939.        gconstpointer  ptr1,
  940.        gconstpointer  ptr2)
  941. {
  942.   GtkCListRow *row1 = (GtkCListRow *) ptr1;
  943.   GtkCListRow *row2 = (GtkCListRow *) ptr2;
  944.  
  945.   /* Get the data for the row */
  946.   PInfo *row1_pinfo = row1->data;
  947.   PInfo *row2_pinfo = row2->data;
  948.  
  949.   /* Want to sort on the date field */
  950.  
  951.   if (row2_pinfo->instime < row1_pinfo->instime)
  952.     {
  953.       return -1;
  954.     }
  955.  
  956.   if (row2_pinfo->instime > row1_pinfo->instime)
  957.     {
  958.       return 1;
  959.     }
  960.  
  961.   return 0;
  962. }
  963.  
  964. static void 
  965. clist_click_column (GtkCList *clist, 
  966.             gint      column, 
  967.             gpointer  data)
  968. {
  969.   if (column == 1)
  970.     {
  971.       gtk_clist_set_compare_func (clist, date_sort);
  972.     }
  973.   else
  974.     {
  975.       gtk_clist_set_compare_func (clist, NULL); /* Set back to default */
  976.     }
  977.  
  978.   if (column == clist->sort_column)
  979.     {
  980.       if (clist->sort_type == GTK_SORT_ASCENDING)
  981.     clist->sort_type = GTK_SORT_DESCENDING;
  982.       else
  983.     clist->sort_type = GTK_SORT_ASCENDING;
  984.     }
  985.   else
  986.     gtk_clist_set_sort_column (clist, column);
  987.   
  988.   gtk_clist_sort (clist);
  989. }
  990.  
  991.  
  992. static GtkWidget *
  993. gimp_plugin_desc (void)
  994. {
  995.   GtkWidget  *button;
  996.   GtkWidget  *hbox, *searchhbox, *vbox;
  997.   GtkWidget  *label, *notebook, *swindow;
  998.   gchar      *clabels[4];
  999.  
  1000.   gimp_ui_init ("plugindetails", FALSE);
  1001.  
  1002.   plugindesc = g_new0 (PDesc, 1);
  1003.  
  1004.   /* the dialog box */
  1005.   plugindesc->dlg =
  1006.     gimp_dialog_new (_("Plugin Descriptions"), "plugindetails",
  1007.              gimp_standard_help_func, "filters/plugindetails.html",
  1008.              GTK_WIN_POS_MOUSE,
  1009.              FALSE, TRUE, TRUE,
  1010.  
  1011.              _("Search by Name"), dialog_search_callback,
  1012.              plugindesc, NULL, NULL, FALSE, FALSE,
  1013.              _("Close"), dialog_close_callback,
  1014.              plugindesc, NULL, NULL, TRUE, TRUE,
  1015.  
  1016.              NULL);
  1017.  
  1018.   plugindesc->details_showing = FALSE;
  1019.  
  1020.   gtk_signal_connect (GTK_OBJECT (plugindesc->dlg), "destroy",
  1021.                       GTK_SIGNAL_FUNC (dialog_close_callback),
  1022.               plugindesc);
  1023.  
  1024.   /* hbox : left=notebook ; right=description */
  1025.   
  1026.   plugindesc->paned = hbox = gtk_hpaned_new ();
  1027.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (plugindesc->dlg)->vbox), 
  1028.               hbox, TRUE, TRUE, 0);
  1029.   gtk_widget_show (hbox);
  1030.  
  1031.   gtk_paned_set_handle_size (GTK_PANED (hbox), 0);
  1032. #if !defined (GTK_CHECK_VERSION) || !GTK_CHECK_VERSION (1, 3, 0)
  1033.   gtk_paned_set_gutter_size (GTK_PANED (hbox), 0);
  1034. #endif
  1035.   /* left = vbox : the list and the search entry */
  1036.   
  1037.   plugindesc->left_paned = vbox = gtk_vbox_new (FALSE, 0);
  1038.   gtk_container_set_border_width (GTK_CONTAINER (vbox), 3); 
  1039.   gtk_paned_pack1 (GTK_PANED (hbox), vbox, FALSE, FALSE);
  1040.   gtk_widget_show (vbox);
  1041.  
  1042.   /* left = notebook */
  1043.  
  1044.   notebook = gtk_notebook_new ();
  1045.   gtk_box_pack_start (GTK_BOX (vbox), notebook, TRUE, TRUE, 0);
  1046.  
  1047.   /* list : list in a scrolled_win */
  1048.   
  1049.   clabels[0] = g_strdup (_("Name")); 
  1050.   clabels[1] = g_strdup (_("Ins Date")); 
  1051.   clabels[2] = g_strdup (_("Menu Path")); 
  1052.   clabels[3] = g_strdup (_("Image Types")); 
  1053.   plugindesc->clist = gtk_clist_new_with_titles (4, clabels); 
  1054.  
  1055.   gtk_signal_connect (GTK_OBJECT (plugindesc->clist), "click_column",
  1056.               GTK_SIGNAL_FUNC (clist_click_column),
  1057.               NULL);
  1058.   gtk_clist_column_titles_show (GTK_CLIST (plugindesc->clist));
  1059.   swindow = gtk_scrolled_window_new (NULL, NULL);
  1060.   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
  1061.                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  1062.   gtk_clist_set_selection_mode (GTK_CLIST (plugindesc->clist),
  1063.                     GTK_SELECTION_BROWSE);
  1064.  
  1065.   gtk_widget_set_usize (plugindesc->clist, DBL_LIST_WIDTH, DBL_HEIGHT);
  1066.   gtk_signal_connect (GTK_OBJECT (plugindesc->clist), "select_row",
  1067.               GTK_SIGNAL_FUNC (procedure_clist_select_callback),
  1068.               plugindesc);
  1069.   
  1070.   label = gtk_label_new (_("List View"));
  1071.   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), swindow, label);
  1072.   gtk_container_add (GTK_CONTAINER (swindow), plugindesc->clist);
  1073.   gtk_widget_show (plugindesc->clist);
  1074.   gtk_widget_show (swindow);
  1075.  
  1076.   /* notebook->ctree */
  1077.   clabels[0] = g_strdup (_("Menu Path/Name")); 
  1078.   clabels[1] = g_strdup (_("Ins Date")); 
  1079.   clabels[2] = g_strdup (_("Image Types")); 
  1080.   plugindesc->ctree = gtk_ctree_new_with_titles (3, 0, clabels);  
  1081.   plugindesc->ctree_scrolled_win =
  1082.     swindow = gtk_scrolled_window_new (NULL, NULL);
  1083.   gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (swindow),
  1084.                   GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
  1085.   gtk_widget_set_usize (plugindesc->ctree, DBL_LIST_WIDTH, DBL_HEIGHT);
  1086.   gtk_signal_connect (GTK_OBJECT (plugindesc->ctree), "tree_select_row",
  1087.               GTK_SIGNAL_FUNC (procedure_ctree_select_callback),
  1088.               plugindesc);
  1089.   gtk_clist_set_column_auto_resize (GTK_CLIST (plugindesc->ctree), 0, TRUE);
  1090.   gtk_clist_set_column_auto_resize (GTK_CLIST (plugindesc->ctree), 1, TRUE);
  1091.   gtk_clist_set_column_auto_resize (GTK_CLIST (plugindesc->ctree), 2, TRUE);
  1092.   gtk_clist_column_titles_passive (GTK_CLIST (plugindesc->ctree));
  1093.  
  1094.   label = gtk_label_new (_("Tree View"));
  1095.   gtk_notebook_append_page (GTK_NOTEBOOK (notebook), swindow, label);
  1096.   gtk_container_add (GTK_CONTAINER (swindow), plugindesc->ctree);
  1097.  
  1098.   gtk_widget_show (plugindesc->ctree);
  1099.   gtk_widget_show (swindow);
  1100.  
  1101.   gtk_signal_connect_after (GTK_OBJECT (notebook), "switch_page",
  1102.                 GTK_SIGNAL_FUNC (page_select_callback),
  1103.                 plugindesc);
  1104.   
  1105.   gtk_widget_show (notebook);
  1106.  
  1107.   /* search entry & details button */
  1108.  
  1109.   searchhbox = gtk_hbox_new (FALSE, 4);
  1110.   gtk_box_pack_start (GTK_BOX (vbox),
  1111.               searchhbox, FALSE, FALSE, 0);
  1112.   gtk_widget_show (searchhbox);
  1113.  
  1114.   label = gtk_label_new (_("Search:"));
  1115.   gtk_misc_set_alignment (GTK_MISC (label), 0.0, 0.5);
  1116.   gtk_box_pack_start (GTK_BOX (searchhbox), 
  1117.               label, FALSE, FALSE, 0);
  1118.   gtk_widget_show(label);
  1119.  
  1120.   plugindesc->search_entry = gtk_entry_new ();
  1121.   gtk_box_pack_start (GTK_BOX (searchhbox), 
  1122.               plugindesc->search_entry, TRUE, TRUE, 0);
  1123.   gtk_widget_show (plugindesc->search_entry);
  1124.  
  1125.   button = gtk_button_new_with_label (_("Details >>"));
  1126.   gtk_misc_set_padding (GTK_MISC (GTK_BIN (button)->child), 2, 0);
  1127.   gtk_signal_connect (GTK_OBJECT (button), "clicked",
  1128.               GTK_SIGNAL_FUNC (details_callback),
  1129.               plugindesc);
  1130.   gtk_box_pack_start (GTK_BOX (searchhbox), button,
  1131.               FALSE, FALSE, 0);
  1132.   gtk_widget_show (button);
  1133.  
  1134.   /* right = description */
  1135.   /* the right description is build on first click of the Details button */
  1136.  
  1137.   /* now build the list */
  1138.   dialog_search_callback (NULL, (gpointer) plugindesc);
  1139.  
  1140.   gtk_clist_set_selection_mode (GTK_CLIST (plugindesc->ctree),
  1141.                 GTK_SELECTION_BROWSE);
  1142.  
  1143.   gtk_widget_show (plugindesc->clist); 
  1144.   gtk_widget_show (plugindesc->dlg);
  1145.   
  1146.   gtk_clist_select_row (GTK_CLIST (plugindesc->clist), 0, 0);
  1147.   gtk_clist_moveto (GTK_CLIST (plugindesc->clist), 0, 0, 0.0, 0.0);
  1148.  
  1149.   plugindesc->c1size = GTK_PANED (plugindesc->paned)->child1_size;
  1150.   GTK_PANED (plugindesc->paned)->child1_resize = TRUE;
  1151.  
  1152.   return plugindesc->dlg;
  1153. }
  1154.